require 'MeCab'
$KCODE = 'u'

#Functionクラス
class Function
  attr_reader :ex_array, :matched

  katakana = %w{ア イ ウ エ オ} ; hiragana = %w{あ い う え お}  
  katakana += %w{カ キ ク ケ コ}; hiragana += %w{か き く け こ} 
  katakana += %w{サ シ ス セ ソ}; hiragana += %w{さ し す せ そ} 
  katakana += %w{タ チ ツ テ ト}; hiragana += %w{た ち つ て と} 
  katakana += %w{ナ ニ ヌ ネ ノ}; hiragana += %w{な に ぬ ね の} 
  katakana += %w{ハ ヒ フ ヘ ホ}; hiragana += %w{は ひ ふ へ ほ} 
  katakana += %w{マ ミ ム メ モ}; hiragana += %w{ま み む め も} 
  katakana += %w{ヤ ユ ヨ}      ; hiragana += %w{や ゆ よ}     
  katakana += %w{ラ リ ル レ ロ}; hiragana += %w{ら り る れ ろ} 
  katakana += %w{ワ ヲ ン}      ; hiragana += %w{わ を ん}     
  katakana += %w{ァ ィ ゥ ェ ォ}; hiragana += %w{ぁ ぃ ぅ ぇ ぉ} 
  katakana += %w{ッ}            ; hiragana += %w{っ}   
  katakana += %w{ャ ュ ョ}      ; hiragana += %w{ゃ ゅ ょ}   
  katakana += %w{ヮ}           ; hiragana += %w{ゎ}   
  katakana += %w{ガ ギ グ ゲ ゴ}; hiragana += %w{が ぎ ぐ げ ご} 
  katakana += %w{ザ ジ ズ ゼ ゾ}; hiragana += %w{ざ じ ず ぜ ぞ} 
  katakana += %w{ダ ヂ ヅ デ ド}; hiragana += %w{だ ぢ づ で ど} 
  katakana += %w{バ ビ ブ ベ ボ}; hiragana += %w{ば び ぶ べ ぼ} 
  katakana += %w{パ ピ プ ペ ポ}; hiragana += %w{ぱ ぴ ぷ ぺ ぽ}

  $hash = Hash[*[katakana, hiragana].transpose.flatten]
  $regexp = Regexp.new("(#{katakana.join('|')})")

  def initialize(opt = '')
    @mecab = MeCab::Tagger.new(opt)
  end

  #平仮名を片仮名に変換
  def to_hiragana(string)
    return nil if string == nil
    return string.gsub($regexp){|matched|
      $hash[matched]
    }
  end

  #文字列sの形態素解析の結果を改行でスプリットして@str_arrayに格納
  def morph(s = '')
    buf = []
    @mecab.parse(s).split("\n").each{|elm|
      buf.push elm
    }
    return buf
  end

  #ファイルfの中身を全ての行に対してタブでスプリットして@ex_arrayに格納
  def crawl(f)
    @ex_array = []
    File.open(f){|io|
      while line = io.gets
        next if line =~ /^(\s*$|#)/
        @ex_array.push line.chomp.split("\t")
      end
    }
    @ex_array.sort!{|a, b| a[0].to_i <=> b[0].to_i}
  end

  #配列が同じかどうかを調べる．’*’はワイルドカード．
  def matching(seq, wdset)
    if seq[0] == wdset[0] or seq[0] == to_hiragana(wdset[8]) then
      seq.shift 
      wdset.shift
    else
      return false
    end
    seq.each_with_index{|elm ,idx|
      if elm == '*' then
        next
      else
        return false unless elm == wdset[idx]
      end
    }
    return true

  end

  #within_ex：文型の語の切れ目を探す関数
  #seqは文型を一文字単位でスプリットしたもの　配列
  #strは文章をmecabで解析したもの　配列
  def within_ex(str, seq)
    return true if seq == []
    return false if str == ["EOS"]
    #p ['within', str[0], seq[0]]
    s = seq.shift
    wdset = str[0].split(/,|\t/)
    if s[0, 2] == ['*', '*'] then
      seq.unshift s
      return seek_ex(str.dup, seq.dup)
    else
      str.shift
      return within_ex(str.dup, seq.dup) if matching(s.dup, wdset.dup)
    end

    return false
  end

  #seek_ex：文型の文字を例文から探す関数
  #seqは文型を一文字単位でスプリットしたもの　配列
  #strは文章をmecabで解析したもの　配列
  def seek_ex(str, seq)
    return true if seq == []
    return false if str == ["EOS"]
#p ['seek', str[0],  seq[0]]
    s = seq.shift
    while wd = str.shift
      wdset = wd.split(/,|\t/)
      if s[0, 2] == ['*', '*'] then
#        result = seek_ex(str.dup, seq.dup)
#        return result if result
return seek_ex(str.dup, seq.dup)
      else
#        result = within_ex(str.dup, seq.dup) if matching(s.dup, wdset.dup)
#        return result if result
return within_ex(str.dup, seq.dup) if matching(s.dup, wdset.dup)
      end
    end

    return false
  end

  #文型をパーズして配列に格納する
  def seq_array(bunkei)
    buf = []
    bunkei.scan(/\[(.*?)\]/){ buf.push $1.split(/,\s*/) }
    return buf
  end

  #実行（ｓは文章　文字列）
  def run(s = nil, matched = false)
    return 0 unless s

    result = 9
    @matched = []
    @ex_array.each{|ex|
      kyu, grammar, parsed = ex
      next if kyu == nil or grammar == nil or parsed == nil or parsed =~ /(^\s*$|UNKNOWN)/

      exp = Regexp.new(grammar.gsub(/\[.*?\]/, '.*'))
      next unless exp === s

      str = self.morph(s) 
      seq = seq_array(parsed)
      if seek_ex(str.dup, seq.dup) then
        return kyu.to_i unless matched
        result = kyu.to_i if result > kyu.to_i
        @matched.push "#{kyu} #{grammar}"
      end
    }
    return result % 9
  end  
end
